/*
*
* Configs class contains mutable parameters of File Collector. Such as:
* 1) tracking Directories
* 2) ID ranges
*
*/

package org.filecollector.basics.configs;

import org.filecollector.basics.exceptions.IncorrectPathException;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class Configs {
    private static final Pattern PATTERN_STATUS = Pattern.compile("(.)=(.)");
    private final Logger logger = LoggerFactory.getLogger(Configs.class);

    @Getter
    private final ArrayList<Path> trackingDirectories;
    @Getter
    private final Long min;
    @Getter
    private final Long max;
    private final Set<String> flagSet;
    private final Map<String, String> stausMap;

    private String echoConfig() {
        return String.format(
                "\n\tdirectories:%s \n\tID range:[%s-%s] \n\tflags: [%s]",
                String.join(
                        ";\n\t\t",
                        trackingDirectories.stream().map(Path::toString).toList()
                ),
                min,
                max,
                flagSet.stream().toList()
        );
    }

    private Map<String, String> constructMap(Set<String> set) {
        HashMap<String, String> ret = new HashMap<>();
        for (
                var pair : flagSet.stream()
                .filter(a -> PATTERN_STATUS.matcher(a).find())
                .map(a -> a.split("="))
                .collect(Collectors.toSet())
        ) {
            if (pair.length != 2) {
                logger.error("Incorrect status flag. Exception thrown");
                throw new RuntimeException("Syntax error");
            }
            ret.put(pair[0], pair[1]);
        }
        return ret;
    }

    private Configs() {
        var dao = ConfigsSingleton.getDefaultConfigDAO();
        Path defaultPath = Path.of(dao.paths[0]);
        logger.debug("Creating using default configs");

        trackingDirectories = checkDirs(dao.paths);
        if (trackingDirectories.isEmpty()) {
            logger.error(String.format("%s is not a directory. Exception thrown.", dao.paths[0]));
            throw new IncorrectPathException(String.format("%s is not a directory", dao.paths[0]));
        }

        min = dao.getMin();
        max = dao.getMax();
        flagSet = Arrays.stream(dao.flags).collect(Collectors.toSet());
        stausMap = constructMap(flagSet);
        logger.debug(String.format("Created Configs:%s", echoConfig()));
    }

    private ArrayList<Path> checkDirs(String[] paths) {
        logger.debug(
                String.format(
                        "Start checking directories: \n\t%s",
                        String.join(";\n\t", Arrays.stream(paths).toList())
                )
        );
        ArrayList<Path> ret = new ArrayList<>();
        for (var str : paths) {
            Path path = Path.of(str);
            if (!Files.exists(path)) {
                logger.warn(String.format("%s directory doesn't exists. Attempt to create.", str));
                try {
                    Files.createDirectory(path);
                } catch (IOException e) {
                    logger.error(String.format("Directory %s was not created.", str));
                }
                continue;
            }
            if (!Files.isDirectory(path)) {
                logger.warn(String.format("%s is not a directory.", str));
                continue;
            }
            ret.add(path);
        }
        logger.debug(String.format(
                "Directories checking finished. Passed \n\t%s",
                String.join(";\n\t", ret.stream().map(Path::toString).toList())
                )
        );
        return ret;
    }

    private Configs(ConfigsDao dao) {
        ArrayList<Path> dirs = new ArrayList<>();
        min = dao.getMin();
        max = dao.getMax();
        trackingDirectories = dirs;
        flagSet = Arrays.stream(dao.flags).collect(Collectors.toSet());
        stausMap = constructMap(flagSet);
        logger.debug(String.format("Created configs:%s", echoConfig()));
    }

    public static Configs getDefault() {
        return fromDAO(ConfigsSingleton.getDefaultConfigDAO());
    }

    public static Configs fromDAO(ConfigsDao dao) {
        return new Configs(dao);
    }

    public boolean containId(Long id) {
        return (min <= id) && (id <= max);
    }

    public Long getMinRange() {
        return min;
    }

    public Long getMaxRange() {
        return max;
    }

    public boolean containsFlag(String flag) {
        return flagSet.contains(flag);
    }

    public String getStatus(String statusKey) {
        return stausMap.getOrDefault(statusKey, "NULL");
    }
}
